package com.easygoods.service;

import com.bestvike.stone.core.io.IOUtils;
import com.bestvike.stone.core.io.Path;
import com.bestvike.stone.core.lang.Convert;
import com.bestvike.stone.core.lang.Environment;
import com.bestvike.stone.core.lang.Platform;
import com.bestvike.stone.core.lang.RandomUtils;
import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.core.serialization.URLSerializer;
import com.bestvike.stone.core.vfs.VFSType;
import com.bestvike.stone.core.vfs.VFSUserAuthenticator;
import com.bestvike.stone.core.vfs.VFSUtils;
import com.bestvike.stone.spring.exception.BusinessException;
import com.bestvike.stone.spring.exception.ErrorCode;
import com.bestvike.stone.spring.service.BaseService;
import com.easygoods.dao.FileInfoDao;
import com.easygoods.entity.FileInfo;
import com.easygoods.enums.AttachFileType;
import com.easygoods.properties.UploadProperties;
import com.easygoods.response.UploadResponse;
import com.easygoods.shiro.auth.UserAuth;
import org.apache.commons.vfs2.FileObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author zhq
 * @date 2019/1/10 15:46
 */
@Service
public class FileService extends BaseService {
    @Autowired
    private UploadProperties uploadProperties;
    @Autowired
    private FileInfoDao fileInfoDao;

    //根据文件类型和文件名获取服务器上的存放路径
    private String getFilePath(AttachFileType fileType, String serverFileNameWithExt) {
        String prefix = Environment.OSVersion.getPlatform() == Platform.Windows ? "D://" : StringUtils.EMPTY;//windows 平台放 d 盘
        return fileType == AttachFileType.Image
                ? new Path(prefix + this.uploadProperties.getImagePath()).combine(serverFileNameWithExt).toString()
                : new Path(prefix + this.uploadProperties.getBinaryPath()).combine(serverFileNameWithExt).toString();
    }

    //文件上传
    public UploadResponse upload(byte[] fileData, String fileName, String fileExt) throws IOException {
        //将上传的文件保存到磁盘
        AttachFileType fileType = AttachFileType.fromExt(fileExt);
        String fileId = RandomUtils.randomUUID();
        String serverFileNameWithExt = fileId + fileExt;
        String serverFilePath = this.getFilePath(fileType, serverFileNameWithExt);
        VFSUserAuthenticator authenticator = new VFSUserAuthenticator(VFSType.LOCAL);
        try (FileObject fileObject = VFSUtils.resolveFile(authenticator, serverFilePath);
             OutputStream fileStream = fileObject.getContent().getOutputStream()) {
            IOUtils.write(fileData, fileStream);
        }
        //将文件信息保存到数据库
        FileInfo fileInfo = new FileInfo();
        fileInfo.setId(fileId);
        fileInfo.setFileName(fileName); //上传文件的文件名
        fileInfo.setFileServerName(serverFileNameWithExt); //文件在服务器上的文件名
        fileInfo.setFileType(fileType.name());//图片还是二进制
        fileInfo.setFileExt(fileExt);//上传文件的扩展名
        final UserAuth userAuth = this.getAuth();
        fileInfo.setCreateMemberId(userAuth.getMemberId());
        fileInfo.beforeInsert();
        this.fileInfoDao.insert(fileInfo);

        //返回
        UploadResponse uploadResponse = new UploadResponse();
        uploadResponse.setId(fileInfo.getId());
        uploadResponse.setFileName(fileName);
        return uploadResponse;
    }

    //文件下载
    public void download(String id, HttpServletResponse response) {
        FileInfo fileInfo = this.fileInfoDao.selectByPrimaryKey(id);
        if (fileInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "文件不存在");

        //第一步：设置响应类型
        response.setHeader("Content-Disposition", "attachment;filename=" + URLSerializer.encode(fileInfo.getFileName()));
        response.setContentType("application/force-download");//应用程序强制下载
        AttachFileType fileType = Convert.toType(fileInfo.getFileType(), AttachFileType.class);
        String fullFileName = this.getFilePath(fileType, fileInfo.getFileServerName());
        try (FileObject fileObject = VFSUtils.resolveFile(new VFSUserAuthenticator(VFSType.LOCAL), fullFileName)) {
            if (!fileObject.exists())
                throw new BusinessException(ErrorCode.ERROR_SERVER, "服务端文件已被删除");

            InputStream inputStream = fileObject.getContent().getInputStream();
            try {
                response.setContentLength(inputStream.available());//文件长度
            } catch (Exception ignored) {
            }
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (IOException e) {
            this.log.warn("下载文件失败", e);
        }
    }

    //图片预览
    public void preview(String id, HttpServletResponse response) {
        FileInfo fileInfo = this.fileInfoDao.selectByPrimaryKey(id);
        if (fileInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "文件不存在");

        AttachFileType fileType = Convert.toType(fileInfo.getFileType(), AttachFileType.class);
        if (fileType != AttachFileType.Image)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "该文件类型不支持预览");

        //扩展名
        String ext = fileInfo.getFileExt();
        if (StringUtils.isEmpty(ext))
            ext = "jpeg";
        else if (ext.startsWith("."))
            ext = ext.substring(1);

        response.setContentType("image/" + ext);
        String fullFileName = this.getFilePath(fileType, fileInfo.getFileServerName());
        try (FileObject fileObject = VFSUtils.resolveFile(new VFSUserAuthenticator(VFSType.LOCAL), fullFileName)) {
            if (!fileObject.exists())
                throw new BusinessException(ErrorCode.ERROR_SERVER, "服务端文件已被删除");

            IOUtils.copy(fileObject.getContent().getInputStream(), response.getOutputStream());
        } catch (IOException e) {
            this.log.warn("预览文件失败", e);
        }
    }

    //文件是否存在
    public boolean existsFile(String id) {
        return this.fileInfoDao.exists(id);
    }
}
